ATOM Documentation

← Back to App

ATOM SaaS Test Writing Guide

**Version:** 1.0

**Last Updated:** 2026-02-22

**Purpose:** Templates and examples for writing unit, integration, and E2E tests

---

Table of Contents

  1. Unit Test Template (Frontend)
  2. Unit Test Template (Backend)
  3. Integration Test Template
  4. E2E Test Template
  5. Test Fixtures Guide
  6. Mock Strategy Guide

---

1. Unit Test Template (Frontend)

Frontend unit tests use **Vitest** with **React Testing Library** for component testing.

Example: Agent Governance Unit Test

// src/lib/ai/__tests__/agent-governance.unit.test.ts
/**
 * Agent Governance Unit Tests
 *
 * Tests for agent maturity level permissions, rate limiting, and governance validation.
 * Uses vi.mock for DatabaseService and Redis dependencies.
 */

import { describe, it, expect, beforeEach, vi } from 'vitest';
import { AgentGovernanceService, MaturityLevel, ACTION_COMPLEXITY } from '../agent-governance';
import type { DatabaseService } from '../../database';

/**
 * Mock DatabaseService for agent governance tests
 */
class MockDatabaseService {
  private agents: Map<string, any> = new Map();
  private users: Map<string, any> = new Map();
  queryHistory: string[] = [];

  constructor() {
    // Initialize with test data
    this.agents.set('agent-student', {
      id: 'agent-student',
      tenant_id: 'tenant-123',
      name: 'Student Agent',
      maturity_level: 'student' as MaturityLevel,
      category: 'general',
      confidence_score: 0.4,
    });

    this.agents.set('agent-autonomous', {
      id: 'agent-autonomous',
      tenant_id: 'tenant-123',
      name: 'Autonomous Agent',
      maturity_level: 'autonomous' as MaturityLevel,
      category: 'finance',
      confidence_score: 0.95,
    });
  }

  setAgent(agentId: string, agent: any) {
    this.agents.set(agentId, agent);
  }

  async query(sql: string, params?: any[]): Promise<any> {
    this.queryHistory.push(sql);

    // Handle SELECT queries
    if (sql.includes('SELECT') && sql.includes('agent_registry')) {
      const agentId = params?.[0];
      return this.agents.get(agentId) || null;
    }

    return null;
  }
}

describe('AgentGovernanceService', () => {
  let governance: AgentGovernanceService;
  let mockDb: MockDatabaseService;

  beforeEach(() => {
    mockDb = new MockDatabaseService();
    governance = new AgentGovernanceService(mockDb as any);
  });

  describe('canPerformAction - Student Maturity', () => {
    it('should allow read-only actions', async () => {
      // Arrange
      const tenantId = 'tenant-123';
      const agentId = 'agent-student';
      const action = 'read';

      // Act
      const decision = await governance.canPerformAction(tenantId, agentId, action);

      // Assert
      expect(decision.allowed).toBe(true);
      expect(decision.reason).toBeNull();
    });

    it('should block high-risk actions', async () => {
      // Arrange
      const tenantId = 'tenant-123';
      const agentId = 'agent-student';
      const action = 'delete_code';

      // Act
      const decision = await governance.canPerformAction(tenantId, agentId, action);

      // Assert
      expect(decision.allowed).toBe(false);
      expect(decision.reason).toContain('Insufficient maturity');
      expect(decision.required_maturity).toBe('autonomous');
    });

    it('should allow search actions', async () => {
      // Arrange
      const tenantId = 'tenant-123';
      const agentId = 'agent-student';
      const action = 'search';

      // Act
      const decision = await governance.canPerformAction(tenantId, agentId, action);

      // Assert
      expect(decision.allowed).toBe(true);
      expect(decision.action_complexity).toBe(1);
    });
  });

  describe('canPerformAction - Autonomous Maturity', () => {
    it('should allow all actions including high-risk', async () => {
      // Arrange
      const tenantId = 'tenant-123';
      const agentId = 'agent-autonomous';
      const action = 'delete_code';

      // Act
      const decision = await governance.canPerformAction(tenantId, agentId, action);

      // Assert
      expect(decision.allowed).toBe(true);
      expect(decision.action_complexity).toBe(4);
      expect(decision.reason).toBeNull();
    });

    it('should verify tenant isolation', async () => {
      // Arrange
      const tenant1 = 'tenant-123';
      const tenant2 = 'tenant-456';
      const agentId = 'agent-autonomous';
      const action = 'delete_code';

      // Act
      const decision1 = await governance.canPerformAction(tenant1, agentId, action);
      const decision2 = await governance.canPerformAction(tenant2, agentId, action);

      // Assert - same agent ID for different tenants
      expect(decision1.allowed).toBe(true);
      expect(decision2.allowed).toBe(false); // Agent doesn't exist for tenant2
    });
  });

  describe('getActionComplexity', () => {
    it('should return correct complexity for known actions', () => {
      expect(governance.getActionComplexity('read')).toBe(1);
      expect(governance.getActionComplexity('delete_code')).toBe(4);
      expect(governance.getActionComplexity('send_email')).toBe(3);
    });

    it('should return default complexity for unknown actions', () => {
      const complexity = governance.getActionComplexity('unknown_action');
      expect(complexity).toBe(2); // Default medium complexity
    });
  });
});

Example: Component Unit Test

// src/components/ui/__tests__/button.test.tsx
/**
 * Button Component Unit Tests
 *
 * Tests Button component rendering, interactions, and accessibility.
 */

import { describe, it, expect } from 'vitest';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Button } from '../button';

describe('Button Component', () => {
  it('should render with text', () => {
    render(<Button>Click me</Button>);

    expect(screen.getByRole('button')).toHaveTextContent('Click me');
  });

  it('should call onClick when clicked', async () => {
    const handleClick = vi.fn();
    const user = userEvent.setup();

    render(<Button onClick={handleClick}>Click me</Button>);

    await user.click(screen.getByRole('button'));

    expect(handleClick).toHaveBeenCalledTimes(1);
  });

  it('should be disabled when disabled prop is true', async () => {
    const handleClick = vi.fn();
    const user = userEvent.setup();

    render(<Button onClick={handleClick} disabled>Click me</Button>);

    await user.click(screen.getByRole('button'));

    expect(screen.getByRole('button')).toBeDisabled();
    expect(handleClick).not.toHaveBeenCalled();
  });

  it('should have accessible name', () => {
    render(<Button aria-label="Close dialog">X</Button>);

    expect(screen.getByRole('button', { name: 'Close dialog' })).toBeInTheDocument();
  });
});

Example: API Route Unit Test

// src/app/api/agents/__tests__/route.test.ts
/**
 * Agents API Route Unit Tests
 *
 * Tests agent CRUD API endpoints with mocked database.
 */

import { describe, it, expect, beforeEach, vi } from 'vitest';
import { POST } from '../route';
import { DatabaseService } from '@/lib/database';

// Mock database
vi.mock('@/lib/database');

describe('POST /api/agents', () => {
  let mockDb: DatabaseService;

  beforeEach(() => {
    mockDb = {
      query: vi.fn(),
    } as any;
  });

  it('should create agent with valid input', async () => {
    // Arrange
    const requestBody = {
      name: 'Test Agent',
      maturity: 'supervised',
      category: 'finance',
    };

    vi.mocked(mockDb.query).mockResolvedValue({
      rows: [{ id: 'agent-123', ...requestBody }],
    });

    const request = new Request('http://localhost:3000/api/agents', {
      method: 'POST',
      body: JSON.stringify(requestBody),
    });

    // Act
    const response = await POST(request);
    const data = await response.json();

    // Assert
    expect(response.status).toBe(201);
    expect(data.agent.id).toBe('agent-123');
    expect(data.agent.name).toBe('Test Agent');
    expect(mockDb.query).toHaveBeenCalledWith(
      expect.stringContaining('INSERT INTO agent_registry'),
      expect.arrayContaining([expect.any(String), 'Test Agent', 'supervised'])
    );
  });

  it('should return 400 for missing required fields', async () => {
    // Arrange
    const requestBody = {
      name: 'Test Agent',
      // Missing maturity and category
    };

    const request = new Request('http://localhost:3000/api/agents', {
      method: 'POST',
      body: JSON.stringify(requestBody),
    });

    // Act
    const response = await POST(request);
    const data = await response.json();

    // Assert
    expect(response.status).toBe(400);
    expect(data.error).toContain('required');
  });

  it('should verify tenant isolation', async () => {
    // Arrange - tenant header
    const requestBody = {
      name: 'Test Agent',
      maturity: 'supervised',
      category: 'finance',
    };

    const request = new Request('http://localhost:3000/api/agents', {
      method: 'POST',
      headers: {
        'X-Tenant-ID': 'tenant-123',
      },
      body: JSON.stringify(requestBody),
    });

    vi.mocked(mockDb.query).mockResolvedValue({
      rows: [{ id: 'agent-123', tenant_id: 'tenant-123', ...requestBody }],
    });

    // Act
    const response = await POST(request);
    const data = await response.json();

    // Assert
    expect(data.agent.tenant_id).toBe('tenant-123');
    expect(mockDb.query).toHaveBeenCalledWith(
      expect.stringContaining('tenant_id'),
      expect.arrayContaining([expect.any(String), 'Test Agent', expect.any(String), 'tenant-123'])
    );
  });
});

---

2. Unit Test Template (Backend)

Backend unit tests use **pytest** with **fixtures** for test data generation.

Example: Agent Governance Unit Test (Python)

# backend-saas/tests/unit/test_agent_governance_service.py
"""
Agent Governance Service Unit Tests

Tests for agent maturity level permissions, rate limiting, and governance validation.
Uses pytest fixtures for test data and mock database.
"""

import pytest
from core.agent_governance_service import AgentGovernanceService, MaturityLevel
from unittest.mock import Mock, AsyncMock


@pytest.fixture
def mock_db():
    """Mock database service."""
    db = Mock()
    db.query = Mock()
    return db


@pytest.fixture
def governance_service(mock_db):
    """Agent governance service instance."""
    return AgentGovernanceService(mock_db)


@pytest.fixture
def test_agent():
    """Test agent data."""
    return {
        'id': 'agent-123',
        'tenant_id': 'tenant-456',
        'name': 'Test Agent',
        'maturity_level': 'supervised',
        'category': 'finance',
        'confidence_score': 0.85
    }


class TestAgentGovernanceService:
    """Agent governance service test cases."""

    def test_student_agent_can_perform_read_only_actions(self, governance_service, test_agent, mock_db):
        """Given: Student maturity level
        When: Checking read action permission
        Then: Decision should allow the action
        """
        # Arrange
        test_agent['maturity_level'] = 'student'
        action = 'read'
        tenant_id = 'tenant-456'

        mock_db.query.return_value.fetchone.return_value = test_agent

        # Act
        decision = governance_service.can_perform_action(
            tenant_id,
            test_agent['id'],
            action
        )

        # Assert
        assert decision.allowed is True
        assert decision.reason is None
        assert decision.action_complexity == 1

    def test_student_agent_cannot_perform_high_risk_actions(self, governance_service, test_agent, mock_db):
        """Given: Student maturity level
        When: Checking delete_code action permission
        Then: Decision should block the action with reason
        """
        # Arrange
        test_agent['maturity_level'] = 'student'
        action = 'delete_code'
        tenant_id = 'tenant-456'

        mock_db.query.return_value.fetchone.return_value = test_agent

        # Act
        decision = governance_service.can_perform_action(
            tenant_id,
            test_agent['id'],
            action
        )

        # Assert
        assert decision.allowed is False
        assert 'Insufficient maturity' in decision.reason
        assert decision.required_maturity == 'autonomous'
        assert decision.action_complexity == 4

    def test_autonomous_agent_can_perform_all_actions(self, governance_service, test_agent, mock_db):
        """Given: Autonomous maturity level
        When: Checking high-risk action permission
        Then: Decision should allow all actions
        """
        # Arrange
        test_agent['maturity_level'] = 'autonomous'
        action = 'delete_code'
        tenant_id = 'tenant-456'

        mock_db.query.return_value.fetchone.return_value = test_agent

        # Act
        decision = governance_service.can_perform_action(
            tenant_id,
            test_agent['id'],
            action
        )

        # Assert
        assert decision.allowed is True
        assert decision.reason is None
        assert decision.action_complexity == 4

    def test_get_action_complexity_returns_correct_values(self, governance_service):
        """Given: Known action types
        When: Getting action complexity
        Then: Should return correct complexity level
        """
        # Act & Assert
        assert governance_service.get_action_complexity('read') == 1
        assert governance_service.get_action_complexity('search') == 1
        assert governance_service.get_action_complexity('analyze') == 2
        assert governance_service.get_action_complexity('create') == 3
        assert governance_service.get_action_complexity('delete_code') == 4

    def test_get_action_complexity_returns_default_for_unknown(self, governance_service):
        """Given: Unknown action type
        When: Getting action complexity
        Then: Should return default medium complexity
        """
        # Act
        complexity = governance_service.get_action_complexity('unknown_action')

        # Assert
        assert complexity == 2  # Default medium complexity


class TestTenantIsolation:
    """Tenant isolation test cases."""

    def test_agent_isolated_by_tenant(self, governance_service, mock_db):
        """Given: Same agent ID for different tenants
        When: Checking permissions
        Then: Should query with tenant_id filter
        """
        # Arrange
        tenant1_id = 'tenant-123'
        tenant2_id = 'tenant-456'
        agent_id = 'agent-shared-id'
        action = 'read'

        # Act
        governance_service.can_perform_action(tenant1_id, agent_id, action)
        governance_service.can_perform_action(tenant2_id, agent_id, action)

        # Assert - Both queries should include tenant_id filter
        assert mock_db.query.call_count == 2
        for call in mock_db.query.call_args_list:
            query = call[0][0]
            assert 'tenant_id' in query.lower() or '$1' in query

    def test_cannot_access_agent_from_different_tenant(self, governance_service, mock_db):
        """Given: Agent from tenant-123
        When: tenant-456 tries to access it
        Then: Query should return None (agent not found)
        """
        # Arrange
        tenant_id = 'tenant-456'
        agent_id = 'agent-123'  # Belongs to tenant-123
        action = 'read'

        # Mock query to return None (agent doesn't exist for this tenant)
        mock_db.query.return_value.fetchone.return_value = None

        # Act
        decision = governance_service.can_perform_action(tenant_id, agent_id, action)

        # Assert
        assert decision.allowed is False
        assert 'not found' in decision.reason.lower()

Example: Graduation Exam Unit Test

# backend-saas/tests/unit/test_graduation_exam.py
"""
Graduation Exam Service Unit Tests

Tests for graduation exam execution, readiness calculation, and validation.
"""

import pytest
from datetime import datetime, timedelta
from core.graduation_exam import GraduationExamService
from core.models import Episode, Agent


@pytest.fixture
def mock_db():
    """Mock database session."""
    db = Mock()
    db.query = Mock()
    return db


@pytest.fixture
def exam_service(mock_db):
    """Graduation exam service instance."""
    return GraduationExamService(mock_db)


@pytest.fixture
def test_episodes():
    """Test episode data for readiness calculation."""
    base_time = datetime.utcnow()

    return [
        Episode(
            id='episode-1',
            agent_id='agent-123',
            task_description='Task 1',
            outcome='success',
            success=True,
            constitutional_violations=[],
            zero_intervention=True,
            created_at=base_time - timedelta(hours=5)
        ),
        Episode(
            id='episode-2',
            agent_id='agent-123',
            task_description='Task 2',
            outcome='success',
            success=True,
            constitutional_violations=['minor_safety_issue'],
            zero_intervention=False,
            created_at=base_time - timedelta(hours=4)
        ),
        Episode(
            id='episode-3',
            agent_id='agent-123',
            task_description='Task 3',
            outcome='failure',
            success=False,
            constitutional_violations=[],
            zero_intervention=True,
            created_at=base_time - timedelta(hours=3)
        ),
    ]


class TestGraduationReadiness:
    """Graduation readiness calculation tests."""

    def test_calculate_readiness_for_intern_to_supervised(self, exam_service, test_episodes):
        """Given: 30 episodes with mixed outcomes
        When: Calculating readiness for intern -> supervised
        Then: Should return readiness score >= 0.80 threshold
        """
        # Arrange
        agent_id = 'agent-123'
        target_maturity = 'supervised'
        episode_count = 30

        # Mock episodes query
        exam_service.db.query.return_value.filter.return_value.limit.return_value.all.return_value = test_episodes * 10

        # Act
        readiness = exam_service.calculate_readiness(agent_id, target_maturity, episode_count)

        # Assert
        assert 0.0 <= readiness.readiness_score <= 1.0
        assert readiness.zero_intervention_ratio >= 0.0
        assert readiness.avg_constitutional_score >= 0.0
        assert readiness.episode_count == 30

        # Verify threshold: intern -> supervised requires 80% readiness
        if readiness.readiness_score >= 0.80:
            assert readiness.can_graduate is True

    def test_readiness_requires_min_30_episodes(self, exam_service):
        """Given: Less than 30 episodes
        When: Calculating readiness
        Then: Should raise error for insufficient episodes
        """
        # Arrange
        agent_id = 'agent-123'
        target_maturity = 'supervised'
        episode_count = 15  # Below minimum

        # Act & Assert
        with pytest.raises(ValueError, match='at least 30 episodes'):
            exam_service.calculate_readiness(agent_id, target_maturity, episode_count)


class TestGraduationExam:
    """Graduation exam execution tests."""

    @pytest.mark.asyncio
    async def test_execute_exam_success(self, exam_service, mock_db):
        """Given: Agent with 90% readiness
        When: Executing graduation exam
        Then: Should pass and update agent maturity
        """
        # Arrange
        agent_id = 'agent-123'
        target_maturity = 'supervised'

        # Mock readiness calculation
        exam_service.calculate_readiness = Mock(return_value=Mock(
            readiness_score=0.90,
            can_graduate=True,
            zero_intervention_ratio=0.85,
            avg_constitutional_score=95.0,
            episode_count=30
        ))

        # Mock agent update
        mock_db.query.return_value.filter.return_value.first.return_value = Mock(
            id=agent_id,
            maturity_level='intern'
        )

        # Act
        result = await exam_service.execute_exam(agent_id, target_maturity, 30)

        # Assert
        assert result.passed is True
        assert result.readiness_score == 0.90
        assert result.new_maturity == 'supervised'
        assert result.previous_maturity == 'intern'

    @pytest.mark.asyncio
    async def test_execute_exam_failure_below_threshold(self, exam_service):
        """Given: Agent with 70% readiness (below 80% threshold)
        When: Executing graduation exam
        Then: Should fail and not update maturity
        """
        # Arrange
        agent_id = 'agent-123'
        target_maturity = 'supervised'

        # Mock readiness below threshold
        exam_service.calculate_readiness = Mock(return_value=Mock(
            readiness_score=0.70,
            can_graduate=False,
            zero_intervention_ratio=0.60,
            avg_constitutional_score=85.0,
            episode_count=30
        ))

        # Act
        result = await exam_service.execute_exam(agent_id, target_maturity, 30)

        # Assert
        assert result.passed is False
        assert result.readiness_score == 0.70
        assert result.failure_reason contains('below threshold')

---

3. Integration Test Template

Integration tests verify interactions between components (e.g., service + database).

Example: Hosting Integration Test

# backend-saas/tests/integration/test_hosting_integration.py
"""
Hosting Service Integration Tests

Tests ATOM Cloud hosting provisioning with mocked atom-cli.
"""

import pytest
from unittest.mock import patch, AsyncMock
from core.hosting_service import HostingService
from core.models import HostingProject, Tenant


@pytest.fixture
def mock_db_session():
    """Mock database session."""
    session = Mock()
    session.add = Mock()
    session.commit = Mock()
    session.flush = Mock()
    session.refresh = Mock()
    return session


@pytest.fixture
def test_tenant():
    """Test tenant data."""
    return Tenant(
        id='tenant-123',
        subdomain='testcompany',
        plan_tier='Team'
    )


@pytest.fixture
def hosting_service(mock_db_session):
    """Hosting service instance."""
    return HostingService(mock_db_session)


class TestHostingProvisioning:
    """Hosting provisioning integration tests."""

    @pytest.mark.asyncio
    @patch('subprocess.run')
    async def test_provision_tenant_app_success(self, mock_run, hosting_service, test_tenant, mock_db_session):
        """Given: Team tenant with governance approval
        When: Provisioning ATOM Cloud app
        Then: Should create app, configure resources, return success
        """
        # Arrange
        project_name = 'test-app'
        mock_run.return_value = Mock(
            stdout=b'App created successfully',
            stderr=b'',
            returncode=0
        )

        # Mock governance check
        with patch.object(hosting_service, '_check_governance_approval') as mock_gov:
            mock_gov.return_value = True

            # Act
            result = await hosting_service.provision_tenant_app(
                test_tenant.id,
                project_name
            )

            # Assert
            assert result.success is True
            assert result.app_name.startswith(f'{test_tenant.subdomain}-{project_name}')
            assert result.app_url is not None

            # Verify atom-cli commands were called
            assert mock_run.call_count >= 2  # create + config commands

            # Verify database record created
            assert mock_db_session.add.called
            assert mock_db_session.commit.called

    @pytest.mark.asyncio
    @patch('subprocess.run')
    async def test_provision_enforces_quota_limits(self, mock_run, hosting_service, test_tenant):
        """Given: Free tier tenant with 1 app limit
        When: Provisioning second app
        Then: Should reject with quota exceeded error
        """
        # Arrange
        project_name = 'second-app'  # Already has 1 app

        # Mock existing app count
        with patch.object(hosting_service, '_get_app_count') as mock_count:
            mock_count.return_value = 1  # At limit

            # Act & Assert
            with pytest.raises(ValueError, match='quota exceeded'):
                await hosting_service.provision_tenant_app(
                    test_tenant.id,
                    project_name
                )

    @pytest.mark.asyncio
    @patch('subprocess.run')
    async def test_provision_fails_on_atom_cli_error(self, mock_run, hosting_service, test_tenant):
        """Given: ATOM Cloud CLI error
        When: Provisioning app
        Then: Should handle error gracefully, rollback database transaction
        """
        # Arrange
        project_name = 'test-app'
        mock_run.return_value = Mock(
            stdout=b'',
            stderr=b'Error: Authentication required',
            returncode=1  # Non-zero = error
        )

        # Act & Assert
        with pytest.raises(Exception, match='ATOM Cloud provisioning failed'):
            await hosting_service.provision_tenant_app(
                test_tenant.id,
                project_name
            )

        # Verify rollback was called
        assert hosting_service.db.rollback.called


class TestCostEstimation:
    """Cost estimation integration tests."""

    def test_estimate_cost_returns_accurate_pricing(self, hosting_service):
        """Given: Resource configuration
        When: Estimating monthly cost
        Then: Should return accurate cost breakdown
        """
        # Arrange
        cpu = 'dedicated-cpu-1x'
        memory_gb = 2
        storage_gb = 10

        # Act
        estimate = hosting_service.estimate_cost(cpu, memory_gb, storage_gb)

        # Assert
        assert estimate.cpu_cost_per_hour > 0
        assert estimate.memory_cost_per_hour > 0
        assert estimate.storage_cost_per_month > 0
        assert estimate.total_monthly_cost > 0

        # Verify calculation formula
        expected_monthly = (
            (estimate.cpu_cost_per_hour * 24 * 30) +
            (estimate.memory_cost_per_hour * 24 * 30) +
            estimate.storage_cost_per_month
        )
        assert abs(estimate.total_monthly_cost - expected_monthly) < 0.01

---

4. E2E Test Template

E2E tests verify complete user workflows using **Playwright**.

Example: Complete Agent Workflow E2E Test

// tests/e2e/agent-workflow.test.ts
/**
 * Agent Workflow E2E Tests
 *
 * Tests complete user journey from signup to agent execution.
 */

import { test, expect } from '@playwright/test';

test.describe('Agent Workflow', () => {
  test.beforeEach(async ({ page }) => {
    // Navigate to app
    await page.goto('/');

    // Login (or signup if needed)
    await page.click('[data-testid="login-button"]');
    await page.fill('[name="email"]', 'test@example.com');
    await page.fill('[name="password"]', 'SecurePass123!');
    await page.click('[type="submit"]');
    await page.waitForURL('/dashboard');
  });

  test('should create agent and execute skill successfully', async ({ page }) => {
    // Navigate to agents page
    await page.click('[data-testid="agents-nav"]');
    await page.waitForURL('/agents');

    // Create new agent
    await page.click('[data-testid="create-agent"]');
    await page.fill('[name="name"]', 'Test Finance Agent');
    await page.selectOption('[name="maturity"]', 'supervised');
    await page.selectOption('[name="category"]', 'finance');
    await page.click('[data-testid="save-agent"]');

    // Verify agent created
    await expect(page.locator('text=Test Finance Agent')).toBeVisible();
    await expect(page.locator('[data-testid="agent-maturity"]')).toHaveText('supervised');

    // Execute skill
    await page.click('[data-testid="execute-skill-button"]');
    await page.selectOption('[name="skill"]', 'reconcile_inventory');
    await page.fill('[name="task-input"]', 'Reconcile SKU-123');
    await page.click('[data-testid="run-skill"]');

    // Wait for execution to complete
    await page.waitForSelector('[data-testid="execution-success"]', { timeout: 30000 });
    await expect(page.locator('[data-testid="execution-result"]')).toBeVisible();

    // Verify episode was recorded
    await page.click('[data-testid="episodes-tab"]');
    await expect(page.locator('text=reconcile_inventory')).toBeVisible();
    await expect(page.locator('[data-testid="episode-status"]')).toHaveText('success');
  });

  test('should enforce governance for high-risk actions', async ({ page }) => {
    // Create student agent (low maturity)
    await page.goto('/agents');
    await page.click('[data-testid="create-agent"]');
    await page.fill('[name="name"]', 'Student Agent');
    await page.selectOption('[name="maturity"]', 'student');
    await page.click('[data-testid="save-agent"]');

    // Try to execute high-risk action (delete_code)
    await page.click('[data-testid="execute-skill-button"]');
    await page.selectOption('[name="skill"]', 'delete_code');
    await page.fill('[name="task-input"]', 'Delete old backup files');
    await page.click('[data-testid="run-skill"]');

    // Verify governance blocks the action
    await expect(page.locator('[data-testid="governance-error"]')).toBeVisible();
    await expect(page.locator('text=Insufficient maturity')).toBeVisible();
    await expect(page.locator('text=student agents cannot delete code')).toBeVisible();
  });

  test('should verify tenant isolation in agents list', async ({ browser }) => {
    // Create two contexts (different tenants)
    const context1 = await browser.newContext();
    const context2 = await browser.newContext();

    const page1 = await context1.newPage();
    const page2 = await context2.newPage();

    // Login as tenant-1
    await page1.goto('/login');
    await page1.fill('[name="email"]', 'tenant1@example.com');
    await page1.fill('[name="password"]', 'password1');
    await page1.click('[type="submit"]');

    // Create agent as tenant-1
    await page1.goto('/agents');
    await page1.click('[data-testid="create-agent"]');
    await page1.fill('[name="name"]', 'Tenant 1 Agent');
    await page1.click('[data-testid="save-agent"]');

    // Login as tenant-2
    await page2.goto('/login');
    await page2.fill('[name="email"]', 'tenant2@example.com');
    await page2.fill('[name="password"]', 'password2');
    await page2.click('[type="submit"]');

    // Navigate to agents page as tenant-2
    await page2.goto('/agents');

    // Verify tenant-2 cannot see tenant-1's agent
    await expect(page2.locator('text=Tenant 1 Agent')).not.toBeVisible();

    // Cleanup
    await context1.close();
    await context2.close();
  });
});

---

5. Test Fixtures Guide

Fixtures provide reusable test data with sensible defaults.

Frontend Fixtures (TypeScript)

// src/__tests__/fixtures/agents.ts
/**
 * Agent test fixtures
 */

import type { Agent, MaturityLevel } from '@/lib/ai/agent-governance';

export function createTestAgent(overrides: Partial<Agent> = {}): Agent {
  const defaults: Agent = {
    id: 'agent-123',
    tenant_id: 'tenant-456',
    name: 'Test Agent',
    maturity_level: 'student' as MaturityLevel,
    category: 'general',
    confidence_score: 0.5,
    created_at: new Date(),
    updated_at: new Date(),
  };

  return { ...defaults, ...overrides };
}

export function createTestAgentList(count: number = 3): Agent[] {
  return Array.from({ length: count }, (_, i) =>
    createTestAgent({
      id: `agent-${i + 1}`,
      name: `Test Agent ${i + 1}`,
    })
  );
}

Backend Fixtures (Python)

# backend-saas/tests/factories.py
"""
Test data factories for backend tests.
"""

from core.models import Agent, Tenant, Episode
from datetime import datetime
from typing import Dict, Any
import uuid


def create_test_agent(**overrides) -> Dict[str, Any]:
    """
    Factory function for test agents with sensible defaults.

    Usage:
        agent = create_test_agent(maturity_level='autonomous')
        agent = create_test_agent(name='Custom Name', category='finance')
    """
    defaults = {
        'id': str(uuid.uuid4()),
        'tenant_id': 'tenant-123',
        'name': 'Test Agent',
        'maturity_level': 'student',
        'category': 'general',
        'confidence_score': 0.5,
        'created_at': datetime.utcnow(),
        'updated_at': datetime.utcnow(),
    }
    return {**defaults, **overrides}


def create_test_tenant(**overrides) -> Dict[str, Any]:
    """
    Factory function for test tenants.
    """
    defaults = {
        'id': str(uuid.uuid4()),
        'subdomain': 'testcompany',
        'plan_tier': 'standard',
        'created_at': datetime.utcnow(),
    }
    return {**defaults, **overrides}


def create_test_episode(**overrides) -> Dict[str, Any]:
    """
    Factory function for test episodes.
    """
    defaults = {
        'id': str(uuid.uuid4()),
        'agent_id': 'agent-123',
        'tenant_id': 'tenant-456',
        'task_description': 'Test task',
        'outcome': 'success',
        'success': True,
        'constitutional_violations': [],
        'zero_intervention': True,
        'constitutional_score': 95.0,
        'confidence_score': 0.9,
        'created_at': datetime.utcnow(),
    }
    return {**defaults, **overrides}


def create_test_episode_list(count: int = 10, **overrides) -> list:
    """
    Factory function for creating multiple test episodes.
    """
    base_time = datetime.utcnow()
    episodes = []

    for i in range(count):
        episode = create_test_episode(
            id=f'episode-{i + 1}',
            task_description=f'Test task {i + 1}',
            created_at=base_time.replace(hour=i)  # Different times
        )
        episodes.append(episode)

    return episodes

Using Fixtures in Tests

# backend-saas/tests/unit/test_episode_service.py
from tests.factories import create_test_agent, create_test_episode, create_test_episode_list


class TestEpisodeService:
    def test_recall_episodes_returns_recent(self, episode_service):
        """Given: 10 episodes created over 10 hours
        When: Recalling last 5 episodes
        Then: Should return 5 most recent episodes
        """
        # Arrange - use factory
        episodes = create_test_episode_list(count=10)
        for episode in episodes:
            episode_service.save(episode)

        # Act
        recalled = episode_service.recall(agent_id='agent-123', limit=5)

        # Assert
        assert len(recalled) == 5
        # Verify most recent (highest hour value)
        assert recalled[0].task_description == 'Test task 10'
        assert recalled[4].task_description == 'Test task 6'

---

6. Mock Strategy Guide

When to Mock vs. Real Implementations

// ✅ Mock: External APIs (LLM providers, OAuth)
it('should call OpenAI API with tenant key', async () => {
  const mockFetch = vi.fn().mockResolvedValue({
    json: async () => ({ choices: [{ message: { content: 'Test response' } }] })
  });

  global.fetch = mockFetch;

  await llmRouter.call('tenant-123', { model: 'gpt-4o', messages: [] });

  expect(mockFetch).toHaveBeenCalledWith(
    'https://api.openai.com/v1/chat/completions',
    expect.objectContaining({
      headers: expect.objectContaining({
        'Authorization': 'Bearer sk-test-tenant-key'
      })
    })
  );
});

// ✅ Mock: Database queries in unit tests
it('should fetch agent from database', async () => {
  vi.spyOn(db, 'query').mockResolvedValue({
    rows: [{ id: 'agent-123', name: 'Test Agent' }]
  });

  const agent = await getAgent('agent-123');

  expect(agent).toEqual({ id: 'agent-123', name: 'Test Agent' });
});

// ❌ Don't Mock: Database integration tests
// Use real SQLite in-memory database instead

// ❌ Don't Mock: Simple utility functions
// Test the real implementation
it('should calculate readiness score correctly', () => {
  const readiness = calculateReadiness({
    zeroInterventionRatio: 0.8,
    avgConstitutionalScore: 95,
    avgConfidenceScore: 0.9,
    successRate: 0.95
  });

  expect(readiness).toBeCloseTo(0.862, 3); // Test real calculation
});

Mock Patterns for External APIs

// Mocking LLM providers
vi.mocked(llmRouter.call).mockResolvedValue({
  choices: [{ message: { role: 'assistant', content: 'Test response' } }],
  usage: { total_tokens: 100 }
});

// Mocking OAuth providers
nock('https://github.com')
  .post('/login/oauth/access_token')
  .reply(200, {
    access_token: 'ghp-test-token',
    token_type: 'bearer'
  });

// Mocking ATOM Cloud CLI
vi.spyOn(child_process, 'exec').mockImplementation((cmd, callback) => {
  if (cmd.includes('atom-cli nodes create')) {
    callback(null, { stdout: 'Node created: test-node' });
  }
});

---

Summary

This guide provides templates and examples for writing tests across the ATOM SaaS platform:

**Frontend (Vitest + React Testing Library):**

  • Unit tests for services, components, API routes
  • Mock vi.fn() for dependencies
  • React Testing Library for component testing

**Backend (pytest):**

  • Unit tests for services, models, API routes
  • Fixtures for reusable test data
  • Mock patches for external dependencies

**Integration Tests:**

  • Service + database interactions
  • Mock external APIs (atom-cli, OAuth)
  • Test complete workflows

**E2E Tests (Playwright):**

  • Complete user journeys
  • Multi-page workflows
  • Tenant isolation verification

**Next Steps:**

  • Use templates as starting point for new tests
  • Adapt examples to your specific feature
  • Follow platform patterns (tenant isolation, BYOK, governance)
  • Run tests locally before committing

---

*Document version: 1.0*

*Last updated: 2026-02-22*

*Maintained by: Platform Engineering Team*